#include "FileStream.h"

uint32 FileStream::SeekFile( uint64 filePos , uint64& newPos , SEEK_POSITION seekPos )
{
	int32 code = CheckFileInvalid();

	if( code != R_SUCCEED )
		return code;

	DWORD moveMethod = SEEK_CUR;

	if( seekPos == BEGIN )
		moveMethod = SEEK_SET;
	else if( seekPos == END )
		moveMethod = SEEK_END;
	else
		moveMethod = SEEK_CUR;

	LARGE_INTEGER movePosition = { 0 };
	LARGE_INTEGER newPosition  = { 0 };

	movePosition.QuadPart = filePos;

	if( ! ::SetFilePointerEx( m_file , movePosition , &newPosition , moveMethod ) )
	{
		return E_FILEPOSITION;
	}

	newPos = newPosition.QuadPart;

	return R_SUCCEED;
}

int32 FileStream::CheckFileInvalid()
{
	if( m_file == INVALID_HANDLE_VALUE )
		return E_FILE;
	return R_SUCCEED;
}

FileStream::FileStream() : m_file(INVALID_HANDLE_VALUE)
{

}

int32 FileStream::Open( ctchar* fileName , uint32 mode /*= READ | SHARE_READ */ )
{
	DWORD accessMode  = 0;
	DWORD shareMode   = 0;
	DWORD disposition = 0;

	uint32 modeDisposition = (mode) & (0xFF);
	uint32 modeAccess      = (mode) & (0xFF00);
	uint32 modeShare       = (mode) & (0xFF0000);

	if(modeAccess == READ)
		accessMode = GENERIC_READ;
	else if( modeAccess == WRITE )
		accessMode = GENERIC_WRITE;
	else
		accessMode = GENERIC_ALL;

	if(modeDisposition == CREATE)
		disposition = CREATE_ALWAYS;
	else
		disposition= OPEN_EXISTING;

	if( modeShare == FILE_SHARE_READ )
		shareMode = FILE_SHARE_READ;
	else
		shareMode = FILE_SHARE_WRITE;

	m_file = ::CreateFile( fileName , accessMode , shareMode , 0 , disposition , FILE_ATTRIBUTE_NORMAL , NULL );

	if( CheckFileInvalid() != R_SUCCEED )
		return E_OPEN_FILE;
	return R_SUCCEED;
}

int32 FileStream::CanRead()
{
	return CheckFileInvalid();
}

int32 FileStream::CanSeek()
{
	return CheckFileInvalid();
}

int32 FileStream::HaveEOS()
{
	return CheckFileInvalid();
}

int32 FileStream::Seek( uint64 position , SEEK_POSITION flag /*= CURRENT */ )
{
	uint64 newPosition = 0;
	return SeekFile( position , newPosition , flag );
}

int32 FileStream::GetPosition( uint64& position )
{
	return SeekFile( 0 , position , CURRENT );
}

int32 FileStream::CanWrite()
{
	return CheckFileInvalid();
}

int32 FileStream::GetSize( uint64& size )
{
	LARGE_INTEGER fileSize = {0};
	if( ! GetFileSizeEx( m_file , &fileSize ) )
	{
		return E_FILESIZE;
	}

	size = fileSize.QuadPart;
	return R_SUCCEED;
}

int32 FileStream::Read( byte* block , uint32 blockSize , uint32& fileRead )
{
	DWORD readSize = 0;

	int32 code = CheckFileInvalid();

	if( code != R_SUCCEED )
		return code;

	if( !::ReadFile( m_file , block , blockSize , &readSize , 0 ) )
		return E_READFILE;
	fileRead = readSize;
	return R_SUCCEED;
}

int32 FileStream::Write( byte* block , uint32 blockSize , uint32& fileWrite )
{
	DWORD writeSize = 0;

	int32 code = CheckFileInvalid();
	if( code != R_SUCCEED )
		return code;
	if( !::WriteFile( m_file , block , blockSize , &writeSize , 0 ) )
		return E_WRITEFILE;
	fileWrite = writeSize;
	return R_SUCCEED;
}

int32 FileStream::EndOfStream()
{
	int32 code = R_SUCCEED;

	uint64 currentPos = 0;
	uint64 fileSize   = 0;
	code = GetPosition( currentPos );

	if( code != R_SUCCEED )
		return code;

	code = GetSize(fileSize);

	if(code != R_SUCCEED )
		return code;

	if( currentPos == fileSize )
		return R_SUCCEED;

	return R_NOT_ENDOFSTREAM;
}

int32 FileStream::Close()
{

	if(CheckFileInvalid() == E_FILE)
		return R_SUCCEED;

	if( ! ::CloseHandle( m_file ) )
	{
		return E_CLOSEFILE;
	}

	m_file = INVALID_HANDLE_VALUE;

	return R_SUCCEED;
}

int32 FileStream::Flush()
{
	int32 code =  CheckFileInvalid();

	if( code != R_SUCCEED )
		return code;

	if( ! FlushFileBuffers( m_file ) )
		return E_FLUSHFILE;

	return R_SUCCEED;
}

FileStream::~FileStream()
{
	if( CheckFileInvalid() != R_SUCCEED )
		Close();
}